import fs from 'fs';
import path from 'path';
import * as os from "os"

let secretKey = '';
const headerKey = 'qixin';
const headerSuffix = [0x1, 0x1, 0x1, 0xef];
const ignoreSuffix = ['ttf', 'm4a', 'mp3', 'jsc','manifest'];
// 解密方法
export async function decryptFileAndOverwrite(filePath:string) {
    try {
        const headerLen = headerKey.length;
        const startLen = headerLen + headerSuffix.length;
        // 读取文件内容
        const fileContent = await fs.promises.readFile(filePath, 'binary');
        // 检查文件是否符合解密条件
        if (fileContent.length > startLen && fileContent.slice(0, headerLen) === headerKey) {
            const key_v = fileContent.charCodeAt(headerLen);
            const key_alg = fileContent.charCodeAt(headerLen + 1);
            const key_index = fileContent.charCodeAt(headerLen + 2);
            const key_reverse = fileContent.charCodeAt(headerLen + 3);
            if (key_v !== 0x1 || key_alg !== 0x1 || key_index !== 0x1 || key_reverse !== 0xef) {
                // console.log('文件不用解密');
                return;
            }
            const encryptedData = [];
            for (let i = startLen; i < fileContent.length; i++) {
                const kindex = (i - startLen) % secretKey.length;
                const encryptedByte = fileContent.charCodeAt(i) ^ secretKey.charCodeAt(kindex);
                encryptedData.push(encryptedByte);
            }
            // 用加密数据覆盖原文件
            await fs.promises.writeFile(filePath, Buffer.from(encryptedData), 'binary');
            // console.log('加密成功');
        } else {
            // console.log('文件不用解密');
        }
    } catch (error) {
        console.error('解密失败', error);
    }
}
// 递归解密文件夹
export async function decryptFolder(folderPath:string) {
    const files = fs.readdirSync(folderPath);
    for (const fileName of files) {
        const filePath = path.join(folderPath, fileName);
        const stats = fs.lstatSync(filePath);
        if (stats.isDirectory()) {
            await decryptFolder(filePath); // 递归处理子文件夹
        } else if (stats.isFile()) {
            await decryptFileAndOverwrite(filePath); // 调用加密方法处理文件
        }
    }
}
// 加密方法
export async function encryptFileAndOverwrite(filePath:string) {
    try {
        // 判断文件是不是ignoreSuffix里的忽略类型，是就直接return
        if (ignoreSuffix.some(suffix => filePath.endsWith(suffix))) {
            return;
        }
        // 读取文件内容
        const fileContent = await fs.promises.readFile(filePath, 'binary');
        // 添加加密头
        const headerCodes = headerKey.split('').map(char => char.charCodeAt(0));
        headerCodes.push(...headerSuffix);
        const encryptedData = [];
        // 添加加密头到文件内容
        encryptedData.push(...headerCodes);
        // 对文件内容进行加密
        for (let i = 0; i < fileContent.length; i++) {
            const kindex = i % secretKey.length;
            const plainByte = fileContent.charCodeAt(i);
            const encryptedByte = plainByte ^ secretKey.charCodeAt(kindex);
            encryptedData.push(encryptedByte);
        }
        // 用加密数据覆盖原文件
        await fs.promises.writeFile(filePath, Buffer.from(encryptedData), 'binary');
        console.log('加密文件完成:'+filePath);
        
    } catch (error) {
        console.error('加密文件失败:'+filePath, error);
    }
}
// 递归加密文件夹
export async function encryptFolder(folderPath:string) {
    const files = fs.readdirSync(folderPath);
    for (const fileName of files) {
        const filePath = path.join(folderPath, fileName);
        const stats = fs.lstatSync(filePath);
        if (stats.isDirectory()) {
            await encryptFolder(filePath); // 递归处理子文件夹
        } else if (stats.isFile()) {
            await encryptFileAndOverwrite(filePath); // 调用加密方法处理文件
        }
    }
}

/** 将解密代码写入c++中 */
export async function writeInCpp(enginePath:string) {
    await readSecretKey();
    const isIOS = os.type() == 'Darwin';
    // 先获得FileUtils.h和FileUtils.cpp的路径
    let filePathH = '';
    let filePathC = '';
    if (isIOS) {
        const folderPath = enginePath + '/Contents/Resources/resources/3d/engine/native/cocos/platform/';
        filePathH = folderPath + 'FileUtils.h';
        filePathC = folderPath + 'FileUtils.cpp';
    } else {
        const lastSlashIndex = enginePath.lastIndexOf('\\');
        enginePath = enginePath.substring(0, lastSlashIndex+1);
        const folderPath = enginePath + 'resources\\resources\\3d\\engine\\native\\cocos\\platform\\';
        filePathH = folderPath + 'FileUtils.h';
        filePathC = folderPath + 'FileUtils.cpp';
    }
    // console.log('filePathH',filePathH);
    // console.log('filePathCpp',filePathCpp);
    try {
        // 检查fileA是否存在secretKey
        const fileAContent = (await fs.promises.readFile(filePathH, 'utf8')).toString();
        const hasEncryptKey = fileAContent.includes(secretKey);
        if (!hasEncryptKey) {
            const filePathHNew = path.join(Editor.Project.path, "extensions/project-update/config/encrypt/FileUtils.h");
            const filePathCNew = path.join(Editor.Project.path, "extensions/project-update/config/encrypt/FileUtils.cpp");
            const fileBContent = (await fs.promises.readFile(filePathHNew, 'utf8')).toString();
            const newContent = fileBContent.replace(/<SecretKey>/g, secretKey);
            await fs.promises.writeFile(filePathH, newContent, 'utf8');
            const fileBContentC = (await fs.promises.readFile(filePathCNew, 'utf8')).toString();
            await fs.promises.writeFile(filePathC, fileBContentC, 'utf8');
            console.log('写入解密文件成功！');
        } else {
            console.log('解密文件已存在');
        }
    } catch (error) {
        console.error('写入解密文件失败:', error);
        throw error;
    }
}

export async function readSecretKey() {
    try {
        const filePath = path.join(Editor.Project.path, "extensions/project-update/config/encrypt/config.json");
        const json = (await fs.promises.readFile(filePath, 'utf8')).toString();
        const obj = JSON.parse(json);
        secretKey = obj.secretKey;
        // console.log('读取secretKey',secretKey);
        return secretKey;
    } catch (error) {
        console.error('读取secretKey失败:',error);
        throw error;
    }
}